feat(cli): add a read-only doctor command that explains itself - #23
Merged
Conversation
The `--pull` case waited for the pulled content to appear on disk and then asserted that a `pull` line had been logged. `applyRemote` writes the file, saves the manifest and logs last, so the assertions could run inside that window — reliably, once the suite got busy enough. It now waits for the log line, which is the operation's real completion signal, and asserts on the file and the manifest afterwards. Every assertion is kept; this is a stronger wait, not a looser check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two failure modes on a live instance look like a broken instance and are not, and both cost a session an hour of false debugging. An unauthenticated socket is silent rather than angry: Admin accepts the connection, sends ___ready___, and then ignores every command. There is no auth error and no close, so the only symptom is a request timing out — which reads as a slow or broken server. And a self-signed certificate that has expired is harmless here: with allowSelfSigned the identity check is the pinned SHA-256 fingerprint, not the chain. iob-sync keeps working while every other client on the port fails with `certificate has expired`. Neither fact is discoverable from the outside, and anyone hitting them may have nothing but the binary in front of them — no repository, no docs. So the binary has to be the thing that says it. `iob-sync doctor` checks config, certificate, login, socket and a live round-trip, and reports which step is wrong. The round-trip is the point: it is the only check that distinguishes "connected" from "authenticated". Strictly read-only — it never prompts, never writes to the server, and in particular will not pin a certificate on first use, because a diagnostic that changes what it diagnoses cannot tell you where you stand. It emits NDJSON under the existing --json and exits non-zero only on a real failure. The request timeout in client/socket.ts now carries a hint naming the silent auth cause, this command, and the three options AdminSocketClient needs — that string is what someone driving the client directly actually reads. Also removes a stale AGENTS.md gap claiming self-signed certificates are not honoured on the HTTP auth path. client/auth.ts fixed that, and the line was actively misleading. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`feat(doctor)` failed the PR-title check: the scope allowlist in .github/workflows/pr-title.yml is closed, and a new component does not earn a new scope. Nothing in AGENTS.md said so, and the workflow is not somewhere you look before naming a branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Another session working against the live instance lost an hour to a false alarm. Nothing was broken —
listandstatusboth worked, IN-SYNC: 10 — but two things made that impossible to establish from the outside:___ready___, then ignores every command. No auth error, no close. The only symptom isRequest "getObject" timed out after 20000ms., which reads as a slow or broken instance.CN=iobroker, valid Jan 2025 – Jan 2026).iob-syncis genuinely unaffected: withallowSelfSignedthe identity check is the pinned SHA-256 fingerprint, not the chain. Every other client on:8081fails withcertificate has expired, so the instance looks dead from anywhere else.Neither fact is discoverable from the outside, and whoever hits them may have nothing but the binary in front of them — no repository, no docs. So the binary has to be the thing that says it. Docs alone would not have helped here.
What
iob-sync doctor— checks config, certificate, login, socket and a live round-trip, and names the step that is wrong.withContext, which aborts at the first failing step; which step fails is the whole answer.--json; exits non-zero only on a real failure. Verdict is one of three greppable words.Alongside it, the request timeout in
client/socket.tsnow carries a hint naming the silent-auth cause, this command, and the three optionsAdminSocketClientneeds. That string is what someone driving the client directly actually reads — and it also helps an ordinary user whose session expires mid-watch.Notes for review
probeCertificateis now a thin wrapper overprobeCertificateInfo, which additionally returns subject, issuer and the validity window. Existing call sites and their tests are untouched.describeCertificaterather than a real handshake — the interesting cases involve dates in the past, and minting an already-expired certificate per run buys nothing over passing one in.test/fake-server.tsgainsrequireCookieOnSocket, which reproduces the silent failure exactly: accept,___ready___, then drop every command frame.AGENTS.mdgap claiming self-signed certificates are not honoured on the HTTP auth path.client/auth.tsfixed that, and the line was actively misleading — plausibly part of what sent the other session the wrong way.test(watch)commit fixes a genuine ordering race, not a timing one: the--pullcase asserted on a log line thatapplyRemoteemits after the file and manifest it was waiting on. No assertion was loosened.Verification
.iobroker-sync.jsonmtime unchanged after two runs, confirming nothing was pinned or written.🤖 Generated with Claude Code